#include"schedule.h"
#include"log.h"
#include"macro.h"
#include "hook.h" 

namespace sylar{

static sylar::Logger::ptr g_logger = SYLAR_LOG_NAME("system");

static thread_local Scheduler* t_scheduler = nullptr;   // 当前线程的调度器
static thread_local Fiber* t_fiber = nullptr; // 当前调度器对应的协程

Scheduler::Scheduler(size_t threads, bool use_caller, const std::string& name)
    :m_name(name){
    SYLAR_ASSERT(threads>0);
    
    // 使用创建调度器的线程作为一个调用线程
    if(use_caller){
        // 每个线程的默认主协程
        sylar::Fiber::GetThis();
        --threads;

        SYLAR_ASSERT(GetThis()==nullptr); //确保该线程内有唯一的协程调度器
        t_scheduler = this;
        /// 因为使用了use——caller所以main协程不能参与到run方法，需要有一个调度器的主协程
        m_rootFiber.reset(new sylar::Fiber(std::bind(&Scheduler::run, this), 0 ,true)); /// 创建一个用于调度器运行的协程 
        sylar::Thread::SetName(m_name);
        /// 调度器的主协程
        t_fiber = m_rootFiber.get();
        m_rootThread = sylar::GetThreadId();
        m_threadIds.push_back(m_rootThread);
    }else{
        m_rootThread = -1;
    }
    m_threadCount = threads;
    SYLAR_LOG_INFO(g_logger)<<"schedule thread: "<<m_threadCount;
}

Scheduler::~Scheduler(){
    SYLAR_ASSERT(m_stopping);
    if(GetThis()==this){
        t_scheduler = nullptr;
    }
}

Fiber* Scheduler::GetMainFiber(){
    return t_fiber;
}
Scheduler* Scheduler::GetThis(){
    return t_scheduler;
}

void Scheduler::start(){

    MutexType::Lock lock(m_mutex);
    if(!m_stopping){
        return;   /// 启动了直接返回
    }

    m_stopping = false;   /// 启动
    SYLAR_ASSERT(m_threads.empty());   /// 保证线程池为空
    m_threads.resize(m_threadCount);
    for(size_t i=0;i<m_threadCount;i++){
        //将线程重新初始化，保证其运行进入run函数
        m_threads[i].reset(new Thread(std::bind(&Scheduler::run, this), 
                                        m_name+"_"+std::to_string(i)));
        m_threadIds.push_back(m_threads[i]->getId());
//        m_threads[i]->join();
    }
    lock.unlock();//callback的时候还会上锁，所以需要在这里解锁，否则会造成死锁

    /// 实际的启动位置位于stop里
//     if(m_rootFiber){
//         /// 执行run方法
//         m_rootFiber->call();
//         SYLAR_LOG_INFO(g_logger)<<"scheduler m_rootfiber call ";
//     }

}
/**
 * @brief stop分为两种情况，第一种是用了use caller，只需在主协程停止即可, 第二种是没用，在任意非自己线程停止
 * 
 */
void Scheduler::stop(){
    m_autoStop = true;

    if(m_rootFiber && m_threadCount==0 && 
        ((m_rootFiber->getState()==Fiber::TERM)||(m_rootFiber->getState()==Fiber::INIT))){
        SYLAR_LOG_INFO(g_logger) << " scheduler is going to stop: "<<"m_rootfiber state"<<m_rootFiber->getState();
        m_stopping = true;

        if(stopping()) {
            return;   
        }
    }

    /// 不等于-1说明是use_caller线程
    if(m_rootThread!=-1){
        SYLAR_ASSERT(GetThis()== this);
    }else{
        SYLAR_ASSERT(GetThis()!= this);
    }

    m_stopping = true;
    for(size_t i = 0; i < m_threadCount; ++i) {
        tickle();     // 类似于信号量，唤醒,tickle结束了线程就结束了
    }

    if(m_rootFiber) {
        // SYLAR_LOG_INFO(g_logger)<<"m_rootFiber call";
        tickle();
    }

    if(m_rootFiber) {
        if(!stopping()) {   // 如果主协程没有停止，任然有协程任务，则调用切换到主协程执行
            // SYLAR_LOG_INFO(g_logger)<<"m_rootFiber call";
            m_rootFiber->call();
        }
    }
    /// 把线程重新启动一下
    std::vector<Thread::ptr> thrs;
    {
        MutexType::Lock lock(m_mutex);
        SYLAR_LOG_INFO(g_logger)<<"swap thread";
        thrs.swap(m_threads);
    }

    for(auto& i : thrs) {
        i->join();
    }

}

void Scheduler::setThis(){
    t_scheduler = this;
}

void Scheduler::run(){
    // SYLAR_LOG_INFO(g_logger)<<" start run()";
    set_hook_enable(true);
    setThis();
    // SYLAR_LOG_INFO(g_logger)<<t_fiber;
    /// 不相等说明是run起来的线程所在的fiber
    if(sylar::GetThreadId()!= m_rootThread){
        t_fiber = Fiber::GetThis().get();
    }
    /// 当我们的调度任务都完成的时候就去做idle
    Fiber::ptr idle_fiber(new Fiber(std::bind(&Scheduler::idle, this)));
    Fiber::ptr cb_fiber;

    FiberAndThread ft;
    while(true) {
        ft.reset();
        //SYLAR_LOG_INFO(g_logger)<<" continue run";
        bool tickle_me = false;
        bool is_active = false;
        {
            MutexType::Lock lock(m_mutex);
            auto it = m_fibers.begin();
            /// 遍历整个协程队列，目的是取出一个协程任务
            while(it != m_fibers.end()) {
                // 没有指定线程并且协程不匹配就跳过，但是需要提醒一下其他的调度器来执行一下
                if(it->thread != -1 && it->thread != sylar::GetThreadId()) {
                    ++it;
                    // 发出信号提醒对应的线程去处理
                    tickle_me = true; 
                    continue;
                }

                SYLAR_ASSERT(it->fiber || it->cb);
                /// 正在执行状态的协程也不用处理
                if(it->fiber && it->fiber->getState() == Fiber::EXEC) {
                    ++it;
                    continue;
                }
                // 当可以处理的时候，去出任务，然后把任务在队列里删除
                ft = *it;
                m_fibers.erase(it++);
                // 活跃状态的线程加1
                ++m_activeThreadCount;
                is_active = true;
                break;
            }
            tickle_me |= it != m_fibers.end();
        }

        if(tickle_me) {
            tickle();
        }
//        if(t_fiber==GetMainFiber()){
//            SYLAR_LOG_INFO(g_logger)<<" run in mainFiber";
//        }
        /// 协程任务处理逻辑
        if(ft.fiber && (ft.fiber->getState() != Fiber::TERM
                        && ft.fiber->getState() != Fiber::EXCEP)) {
            // SYLAR_LOG_INFO(g_logger)<<" run a fiber task";
            ft.fiber->swapIn();
            --m_activeThreadCount;
            /// 该任务有可能中途yield
            if(ft.fiber->getState() == Fiber::READY) {
                schedule(ft.fiber);
            } else if(ft.fiber->getState() != Fiber::TERM
                    && ft.fiber->getState() != Fiber::EXCEP) {
                ft.fiber->m_state = Fiber::HOLD;
                /// 说明协程处于暂停状态？？？协程仍然拥有该线程吗
                /// 为什么没有把hold状态的协程加入队列？？？？？
            }
            ft.reset();
        /// 回调任务处理逻辑
        } else if(ft.cb) {   
            if(cb_fiber) {
                cb_fiber->reset(ft.cb);
            } else {
                cb_fiber.reset(new Fiber(ft.cb));
            }
            ft.reset();
            cb_fiber->swapIn();
            --m_activeThreadCount;
            if(cb_fiber->getState() == Fiber::READY) {
                schedule(cb_fiber);
                cb_fiber.reset();
            } else if(cb_fiber->getState() == Fiber::EXCEP
                    || cb_fiber->getState() == Fiber::TERM) {
                cb_fiber->reset(nullptr);
            } else {//if(cb_fiber->getState() != Fiber::TERM) {
                cb_fiber->m_state = Fiber::HOLD;
                cb_fiber.reset();
            }
        } else {
            if(is_active) {
                --m_activeThreadCount;
                continue;
            }
            if(idle_fiber->getState() == Fiber::TERM) {
                /// idle也term了说明是真正的出口了，调度器即将终止
                SYLAR_LOG_INFO(g_logger) << "idle fiber term";
                break;
            }

            ++m_idleThreadCount;
            idle_fiber->swapIn();
            --m_idleThreadCount;
            if(idle_fiber->getState() != Fiber::TERM
                    && idle_fiber->getState() != Fiber::EXCEP) {
                idle_fiber->m_state = Fiber::HOLD;
                //SYLAR_LOG_INFO(g_logger) << "idle hold";
            }
        }
    }
}


void Scheduler::tickle() {
    SYLAR_LOG_INFO(g_logger) << "tickle";
}

bool Scheduler::stopping() {
    MutexType::Lock lock(m_mutex);
//    SYLAR_LOG_INFO(g_logger) << "schedule stopping?";
    return m_autoStop && m_stopping
            && m_fibers.empty() && m_activeThreadCount == 0;
}

void Scheduler::idle() {
    SYLAR_LOG_INFO(g_logger) << "idle";
    while(!stopping()) {
        sylar::Fiber::YieldToHold();
    }
}

}